package com.jichangxiu.framework.utils;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.toolkit.LambdaUtils;

import java.lang.reflect.Field;
import java.util.*;

public class TreeUtils {

    public static <E, T> List<E> listToTree(List<E> list, SFunction<T, ?> idKeyFunction, SFunction<T, ?> parentKeyFunction, SFunction<T, ?> childKeyFunction) {
        return listToTree(list, LambdaUtils.getName(idKeyFunction), LambdaUtils.getName(parentKeyFunction), LambdaUtils.getName(childKeyFunction));
    }

    public static <T> List<T> listToTree(List<T> list, String idKey, String parentKey, String childKey) {
        List<T> newTreeList = new ArrayList<>();
        Map<Object, T> hash = new HashMap<>();
        try {
            for (T item : list) {
                Field idField = item.getClass().getDeclaredField(idKey);
                idField.setAccessible(true);
                hash.put(idField.get(item), item);
            }
            for (T item : list) {
                Field parentField = item.getClass().getDeclaredField(parentKey);
                parentField.setAccessible(true);
                Object parentId = parentField.get(item);
                if (parentId == null) {
                    newTreeList.add(item);
                } else {
                    T parent = hash.get(parentId);
                    if (parent != null) {
                        Field childrenField = parent.getClass().getDeclaredField(childKey);
                        childrenField.setAccessible(true);
                        List<T> children = (List<T>) childrenField.get(parent);
                        if (children == null) {
                            children = new ArrayList<>();
                            childrenField.set(parent, children);
                        }
                        children.add(item);
                    } else {
                        newTreeList.add(item);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("转换树形异常", e);
        }
        return newTreeList;
    }

    public static <E, T> List<E> pruneTree(List<E> treeList, SFunction<T, ?> childKeyFunction, SFunction<T, ?> keyFunction, String value) {
        return pruneTree(treeList, LambdaUtils.getName(childKeyFunction), LambdaUtils.getName(keyFunction), value);
    }

    public static <T> List<T> pruneTree(List<T> treeList, String childKey, String key, String value) {
        List<T> prunedList = new ArrayList<>();
        try {
            for (T node : treeList) {
                Field keyField = node.getClass().getDeclaredField(key);
                keyField.setAccessible(true);
                String fieldValue = (String) keyField.get(node);
                if (!fieldValue.equals(value)) {
                    Field childField = node.getClass().getDeclaredField(childKey);
                    childField.setAccessible(true);
                    List<T> children = (List<T>) childField.get(node);
                    if (children != null) {
                        children = pruneTree(children, childKey, key, value);
                        childField.set(node, children);
                    }
                    prunedList.add(node);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("修剪树形异常", e);
        }
        return prunedList;
    }

    public static <T> List<T> findTreeNodePath(List<T> treeList, SFunction<T, ?> childKeyFunction, SFunction<T, ?> keyFunction, String value) {
        return findTreeNodePath(treeList, LambdaUtils.getName(childKeyFunction), LambdaUtils.getName(keyFunction), value);
    }

    public static <T> List<T> findTreeNodePath(List<T> treeList, String childKey, String key, String value) {
        List<T> nodePath = new ArrayList<>();
        try {
            findTreeNodePathRecursive(treeList, childKey, key, value, nodePath);
            Collections.reverse(nodePath);
        } catch (Exception e) {
            throw new RuntimeException("查找树形路线异常", e);
        }
        return nodePath;
    }

    private static <T> boolean findTreeNodePathRecursive(List<T> nodes, String childKey, String key, String value, List<T> nodePath) throws Exception {
        for (T node : nodes) {
            Field keyField = node.getClass().getDeclaredField(key);
            keyField.setAccessible(true);
            String fieldValue = (String) keyField.get(node);

            if (fieldValue.equals(value)) {
                nodePath.add(node);
                return true;
            } else {
                Field childField = node.getClass().getDeclaredField(childKey);
                childField.setAccessible(true);
                List<T> children = (List<T>) childField.get(node);
                if (children != null && findTreeNodePathRecursive(children, childKey, key, value, nodePath)) {
                    nodePath.add(node);
                    return true;
                }
            }
        }
        return false;
    }

}
